package com.ucode.springboot.starter.web.exception;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.ucode.tool.base.ResponseResult;
import com.ucode.tool.base.ResultCodeEnum;
import com.ucode.tool.exception.UcodeEntityValidatorException;
import com.ucode.tool.exception.UcodeServiceException;
import com.ucode.tool.exception.UcodeUnauthenticatedException;
import com.ucode.tool.exception.UcodeUnauthorizedException;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

/**
 * 全局异常处理
 * @author: liliang
 * @date: 2019年12月17日 下午2:42:21
 * @firstReview:
 * @lastReview:
 * @desc:
 */
@RestControllerAdvice
public class UcodeExceptionResolver {

    private Log log = LogFactory.get(UcodeExceptionResolver.class);

    /**
     * 未认证(未登录或者token超时)异常处理
     * 注意：安全框架校验异常不会走这里
     * @author liliang
     * @date 2019年12月17日
     * @param response
     * @param e
     */
    @ExceptionHandler(UcodeUnauthenticatedException.class)
    public ResponseResult<Object> unauthenticatedExceptionHandler(HttpServletResponse response, UcodeUnauthenticatedException e) {
        ResponseResult<Object> result = ResponseResult.result(e.getCode(),e.getMessage(),null);
        return result;
    }

    /**
     * 未授权(没有操作权限)异常处理
     * 注意：安全框架校验异常不会走这里
     * @author liliang
     * @date 2019年12月17日
     * @param response
     * @param e
     */
    @ExceptionHandler(UcodeUnauthorizedException.class)
    public ResponseResult<Object> unauthorizedExceptionHandler(HttpServletResponse response,UcodeUnauthorizedException e) {
        ResponseResult<Object> result = ResponseResult.result(e.getCode(),e.getMessage(),null);
        return result;
    }
    
    /**
     * 统一业务异常处理
     * @author liliang
     * @date 2019年12月17日
     * @param response
     * @param e
     */
    @ExceptionHandler(value = UcodeServiceException.class)
    public ResponseResult<Object> serviceExceptionHandler(HttpServletResponse response, UcodeServiceException e) {
        log.error(e);
        ResponseResult<Object> result = ResponseResult.result(e.getCode(),e.getMessage(),null);
        return result;
    }
    
    /**
     * 统一手动参数校验异常处理
     * @author liliang
     * @date 2019年12月17日
     * @param response
     * @param e
     */
    @ExceptionHandler(value = UcodeEntityValidatorException.class)
    public ResponseResult<Object> entityValidatorExceptionHandler(HttpServletResponse response, UcodeEntityValidatorException e) {
        ResponseResult<Object> result = ResponseResult.result(e.getCode(),e.getMessage(),null);
        return result;
    }
    
    /**
     * spring 默认的请求参数类型不匹配异常
     * @author liliang
     * @date 2019年12月18日
     * @param response
     * @param ex
     * @return
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public ResponseResult<Object> requestTypeMismatch(HttpServletResponse response,MethodArgumentTypeMismatchException ex){
        String name = ex.getName();//获取数据类型不匹配参数名称;
        Class<?> requiredType = ex.getRequiredType();//实际要求客户端传递的数据类型
        String msg = ResultCodeEnum.VALIDATE_FAILED.message+"[property:%s -> %s]";
        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.VALIDATE_FAILED.code,String.format(msg, name,requiredType.getTypeName()),null);
        return result;
    }
    
    /**
     * spring 缺少参数异常
     * @author liliang
     * @date 2019年12月18日
     * @param ex
     * @return
     */
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ResponseResult<Object> requestMissingServletRequest(HttpServletResponse response,MissingServletRequestParameterException ex){
        String propertyName = ex.getParameterName();//缺少的参数名称;
        String msg = ResultCodeEnum.VALIDATE_FAILED.message+"[property:%s is not null]";
        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.VALIDATE_FAILED.code,String.format(msg, propertyName),null);
        return result;
    }
    
    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     * @author liliang
     * @date 2019年12月19日
     * @param response
     * @param ex
     */
    @ExceptionHandler(BindException.class)
    public ResponseResult<Object> bindExceptionHandler(HttpServletResponse response,BindException ex) {
        String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        String msg = ResultCodeEnum.VALIDATE_FAILED.message+"[%s]";
        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.VALIDATE_FAILED.code,String.format(msg, message),null);
        return result;
    }
    
    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     * @author liliang
     * @date 2019年12月19日
     * @param response
     * @param ex
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseResult<Object> constraintViolationExceptionHandler(HttpServletResponse response,ConstraintViolationException ex) {
        String message = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
        String msg = ResultCodeEnum.VALIDATE_FAILED.message+"[%s]";
        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.VALIDATE_FAILED.code,String.format(msg, message),null);
        return result;
    }
    
    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常
     * @author liliang
     * @date 2019年12月19日
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseResult<Object> methodArgumentNotValidExceptionHandler(HttpServletResponse response,MethodArgumentNotValidException ex) {
        String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
        String msg = ResultCodeEnum.VALIDATE_FAILED.message+"[%s]";
        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.VALIDATE_FAILED.code,String.format(msg, message),null);
        return result;
    }
    
//    /**
//     * 程序异常统一处理
//     * 注意：这个异常捕捉会造成类似security自身的异常处理无法起效,这里捕捉Exception异常是为了系统报错时，客户端友好提示！
//     * 如果想让类似security自身异常起作用,要么把这里屏蔽掉,要么定义相关异常的捕捉处理
//     * @author liliang
//     * @date 2019年12月17日
//     * @param response
//     * @param e
//     */
//    @ExceptionHandler(value = Exception.class)
//    public ResponseResult<Object> exceptionHandler(HttpServletResponse response, Exception e) {
//        log.error(e);
//        ResponseResult<Object> result = ResponseResult.result(ResultCodeEnum.SERVER_ERROR);
//        return result;
//    }
}